package task

import (
	"sync"
	"time"
)

type taskOption struct {
	startAt     time.Time
	hour        int
	minute      int
	second      int
	millisecond int
	maxRuns     int
	timeout     time.Duration
}
type TaskOption func(*taskOption)

var taskOptionPool = sync.Pool{
	New: func() interface{} {
		return &taskOption{}
	},
}

// releaseTaskOption 释放任务配置(池化重用)
func releaseTaskOption(op *taskOption) {
	if op == nil {
		return
	}
	taskOptionPool.Put(op)
}

// parseTaskOption	解析任务配置(池化重用)
func parseTaskOption(ops ...TaskOption) *taskOption {
	op := taskOptionPool.Get().(*taskOption)
	if op == nil {
		op = &taskOption{}
	}
	op.startAt = time.Time{}
	op.hour = -1
	op.minute = -1
	op.second = -1
	op.millisecond = -1
	op.maxRuns = 0
	op.timeout = defaultTimeout
	for _, f := range ops {
		f(op)
	}
	timeNow := time.Now()
	var (
		y, d, hh, mm, ss, millisecond int
		m                             time.Month
	)
	if op.startAt.IsZero() {
		y, m, d = timeNow.Date()
		hh, mm, ss = timeNow.Clock()
		millisecond = timeNow.Nanosecond() / 1e6
	} else {
		if op.startAt.Location() != time.Local {
			op.startAt = op.startAt.Local()
		}
		y, m, d = op.startAt.Date()
		hh, mm, ss = op.startAt.Clock()
		millisecond = op.startAt.Nanosecond() / 1e6
	}
	if op.hour < 0 {
		op.hour = hh
	}
	if op.minute < 0 {
		op.minute = mm
	}
	if op.second < 0 {
		op.second = ss
	}
	if op.millisecond < 0 {
		op.millisecond = millisecond
	}
	if op.startAt.IsZero() {
		op.startAt = time.Date(y, m, d, op.hour, op.minute, op.second, op.millisecond*1e6, time.Local)
	} else if op.startAt.Hour() != op.hour || op.startAt.Minute() != op.minute || op.startAt.Second() != op.second || op.startAt.Nanosecond() != op.millisecond*1e6 {
		op.startAt = time.Date(y, m, d, op.hour, op.minute, op.second, op.millisecond*1e6, time.Local)
	}
	return op
}

// WithStartAt 设置任务开始的时间(默认为Now)
func WithStartAt(at time.Time) TaskOption {
	return func(op *taskOption) {
		op.startAt = at
	}
}

// WithHour 设置任务时刻：时 (默认为当前小时)
func WithHour(hour int) TaskOption {
	return func(op *taskOption) {
		if hour < 1 {
			hour = 0
		} else if hour > 23 {
			hour = 23
		}
		op.hour = hour
	}
}

// WithMinute 设置任务时刻：分 (默认为当前分钟)
func WithMinute(minute int) TaskOption {
	return func(op *taskOption) {
		if minute < 0 {
			minute = 0
		} else if minute > 59 {
			minute = 59
		}
		op.minute = minute
	}
}

// WithSecond 设置任务时刻：秒 (默认为当前秒)
func WithSecond(second int) TaskOption {
	return func(op *taskOption) {
		if second < 0 {
			second = 0
		} else if second > 59 {
			second = 59
		}
		op.second = second
	}
}

// WithMillisecond 设置任务时刻：毫秒 (默认为当前毫秒)
func WithMillisecond(millisecond int) TaskOption {
	return func(op *taskOption) {
		if millisecond < 0 {
			millisecond = 0
		} else if millisecond > 999 {
			millisecond = 999
		}
		op.millisecond = millisecond
	}
}

// WithRunMax 设置任务最大执行次数 (默认为0:不限制)
func WithRunMax(v int) TaskOption {
	return func(op *taskOption) {
		if v < 0 {
			v = 0
		}
		op.maxRuns = v
	}
}

// WithTimeout 设置任务执行超时时间 (默认为30秒)
func WithTimeout(v time.Duration) TaskOption {
	return func(op *taskOption) {
		if v > 0 {
			op.timeout = v
		}
	}
}

// OnceAt 创建一次性定时任务（基于一个明确时间）
//   - executeAt 调度器内部会统一转换为本地时区后处理，如已过期则调整为当前时间(立即执行)
//   - ops 可通过 TaskOption 指定任务运行的超时时间: WithTimeout
//   - 注意：任务添加后/执行前相同Key任务的添加操作会直接覆盖前一次添加的任务
func (ts *TaskManager) OnceAt(key string, executeAt time.Time, handler TaskHandler, ops ...TaskOption) error {
	op := parseTaskOption(ops...)
	return ts.onceAtWithPC(ts.getEntryPC(3, true), key, executeAt, handler, op)
}
func (ts *TaskManager) onceAtWithPC(pc uintptr, key string, executeAt time.Time, handler TaskHandler, op *taskOption) error {
	if !ts.isRuning.Load() {
		return ErrTaskClosed
	}
	if key == "" {
		return ErrTaskKeyEmpty
	}
	if handler == nil {
		return ErrTaskHandlerNil
	}
	if executeAt.IsZero() {
		executeAt = time.Now()
	} else {
		if executeAt.Location() != time.Local {
			executeAt = executeAt.Local()
		}
		if executeAt.Before(time.Now()) {
			executeAt = time.Now()
		}
	}

	err := ts.addTask(&Task{
		key:       key,
		executeAt: executeAt,
		isOnce:    true,
		handler:   handler,
		timeout:   op.timeout,
		pc:        pc,
	})
	releaseTaskOption(op)
	return err
}

// OnceAfter 创建一次性延迟任务（基于一个延迟时间）
//   - ops 可通过 TaskOption 指定任务运行的开始时间: WithStartAt  超时时间: WithTimeout 等
//   - 注意：任务添加后/执行前相同Key任务的添加操作会直接覆盖前一次添加的任务
func (ts *TaskManager) OnceAfter(key string, delay time.Duration, handler TaskHandler, ops ...TaskOption) error {
	op := parseTaskOption(ops...)
	return ts.onceAtWithPC(ts.getEntryPC(3, true), key, op.startAt.Add(delay), handler, op)
}

// OnceBuffer 创建一次性延迟任务(缓冲模式), 常用于延迟更新任务, 如缓存更新等场景
//   - ops 可通过 TaskOption 指定任务运行的开始时间: WithStartAt  超时时间: WithTimeout 等
//   - 注意：任务添加后/执行前相同Key任务的添加操作将会忽略
func (ts *TaskManager) OnceBuffer(key string, delay time.Duration, handler TaskHandler, ops ...TaskOption) error {
	if !ts.isRuning.Load() {
		return ErrTaskClosed
	}
	if key == "" {
		return ErrTaskKeyEmpty
	}
	if handler == nil {
		return ErrTaskHandlerNil
	}
	op := parseTaskOption(ops...)
	err := ts.addTask(&Task{
		key:       key,
		executeAt: op.startAt.Add(delay),
		isOnce:    true,
		isBuffer:  true,
		handler:   handler,
		timeout:   op.timeout,
		pc:        ts.getEntryPC(3, true),
	})
	releaseTaskOption(op)
	return err
}

// EveryInterval 创建周期性任务,基于一个明确的间隔时间 interval
//   - ops 可通过 TaskOption 指定具体的具体运行时刻：WithHour WithMinute WithSecond WithMillisecond
//   - 注意：任务添加后/执行前相同Key任务的添加操作会直接覆盖前一次添加的任务
func (ts *TaskManager) EveryInterval(key string, interval time.Duration, handler TaskHandler, ops ...TaskOption) error {
	op := parseTaskOption(ops...)
	return ts.everyEveryIntervalWithPC(ts.getEntryPC(3, true), key, interval, handler, op)
}
func (ts *TaskManager) everyEveryIntervalWithPC(pc uintptr, key string, interval time.Duration, handler TaskHandler, op *taskOption) error {
	if !ts.isRuning.Load() {
		return ErrTaskClosed
	}
	if key == "" {
		return ErrTaskKeyEmpty
	}
	if handler == nil {
		return ErrTaskHandlerNil
	}
	// 周期任务间隔不能小于用户配置
	// 任务队列检查最小时间，决定了定时任务定时/间隔精度， 默认 10 毫秒, 最小 1毫秒, 最大 1000毫秒(1秒)
	if interval < ts.options.CheckPeriod {
		interval = ts.options.CheckPeriod
	}
	err := ts.addTask(&Task{
		key:      key,
		interval: newTaskIntervalWithStartAt(taskIntervalFixed, interval, op.startAt),
		runMax:   int32(op.maxRuns),
		handler:  handler,
		timeout:  op.timeout,
		pc:       pc,
	})
	releaseTaskOption(op)
	return err
}

// EverySecond 创建周期性任务（每几秒钟运行一次）
//   - ops 可通过 TaskOption 指定具体的具体运行时刻: WithMillisecond
//   - 注意：任务添加后/执行前相同Key任务的添加操作会直接覆盖前一次添加的任务
func (ts *TaskManager) EverySecond(key string, seconds int, handler TaskHandler, ops ...TaskOption) error {
	if seconds < 1 {
		seconds = 1
	}
	op := parseTaskOption(ops...)
	return ts.everyEveryIntervalWithPC(ts.getEntryPC(3, true), key, time.Second*time.Duration(seconds), handler, op)
}

// EveryMinute 创建周期性任务（每几分钟运行一次）
//   - ops 可通过 TaskOption 指定具体的具体运行时刻：WithSecond WithMillisecond
//   - 注意：任务添加后/执行前相同Key任务的添加操作会直接覆盖前一次添加的任务
func (ts *TaskManager) EveryMinute(key string, minutes int, handler TaskHandler, ops ...TaskOption) error {
	if minutes < 1 {
		minutes = 1
	}
	op := parseTaskOption(ops...)
	return ts.everyEveryIntervalWithPC(ts.getEntryPC(3, true), key, time.Minute*time.Duration(minutes), handler, op)
}

// EveryHour 创建周期性任务（每几小时运行一次）
//   - ops 可通过 TaskOption 指定具体的具体运行时刻：WithMinute WithSecond WithMillisecond
//   - 注意：任务添加后/执行前相同Key任务的添加操作会直接覆盖前一次添加的任务
func (ts *TaskManager) EveryHour(key string, hours int, handler TaskHandler, ops ...TaskOption) error {
	if hours < 1 {
		hours = 1
	}
	op := parseTaskOption(ops...)
	return ts.everyEveryIntervalWithPC(ts.getEntryPC(3, true), key, time.Hour*time.Duration(hours), handler, op)
}

// EveryDay 创建周期性任务（每几天运行一次）
//   - ops 可通过 TaskOption 指定具体的具体运行时刻：WithHour WithMinute WithSecond WithMillisecond
//   - 注意：任务添加后/执行前相同Key任务的添加操作会直接覆盖前一次添加的任务
func (ts *TaskManager) EveryDay(key string, days int, handler TaskHandler, ops ...TaskOption) error {
	if days < 1 {
		days = 1
	}
	op := parseTaskOption(ops...)
	dayHour := time.Hour * 24 // 一天的时间, 如果 time.Hour * 24 * time.Duration(days) 不可预知结果
	return ts.everyEveryIntervalWithPC(ts.getEntryPC(3, true), key, dayHour*time.Duration(days), handler, op)
}

// EveryWeekDay 创建周期性任务（每周几运行一次）
//   - weekDay 0-7, 0/7 => 星期日
//   - ops 可通过 TaskOption 指定具体的具体运行时刻：WithHour WithMinute WithSecond WithMillisecond
//   - 注意：任务添加后/执行前相同Key任务的添加操作会直接覆盖前一次添加的任务
func (ts *TaskManager) EveryWeekDay(key string, weekDay int, handler TaskHandler, ops ...TaskOption) error {
	if !ts.isRuning.Load() {
		return ErrTaskClosed
	}
	if key == "" {
		return ErrTaskKeyEmpty
	}
	if handler == nil {
		return ErrTaskHandlerNil
	}
	op := parseTaskOption(ops...)
	y, m, d := op.startAt.Date()
	err := ts.addTask(&Task{
		key:      key,
		interval: newTaskIntervalWithDate(taskIntervalWeekDay, 0, y, int(m), d, op.hour, op.minute, op.second, op.millisecond, weekDay),
		runMax:   int32(op.maxRuns),
		handler:  handler,
		timeout:  op.timeout,
		pc:       ts.getEntryPC(3, true),
	})
	releaseTaskOption(op)
	return err
}

// EveryMonthDay 创建周期性任务（每月的某一日运行一次）
//   - day: 1-31, 如指定31号，但当月只有30天，则30日执行
//   - ops 可通过 TaskOption 指定具体的具体运行时刻：WithHour WithMinute WithSecond WithMillisecond
//   - 注意：任务添加后/执行前相同Key任务的添加操作会直接覆盖前一次添加的任务
func (ts *TaskManager) EveryMonthDay(key string, day int, handler TaskHandler, ops ...TaskOption) error {
	if !ts.isRuning.Load() {
		return ErrTaskClosed
	}
	if key == "" {
		return ErrTaskKeyEmpty
	}
	if handler == nil {
		return ErrTaskHandlerNil
	}
	op := parseTaskOption(ops...)
	y, m, _ := op.startAt.Date()
	err := ts.addTask(&Task{
		key:      key,
		interval: newTaskIntervalWithDate(taskIntervalMonthDay, 0, y, int(m), day, op.hour, op.minute, op.second, op.millisecond, 0),
		runMax:   int32(op.maxRuns),
		handler:  handler,
		timeout:  op.timeout,
		pc:       ts.getEntryPC(3, true),
	})
	releaseTaskOption(op)
	return err
}

// EveryMonthFirstDay 创建周期性任务（每月第1天运行一次）
//   - ops 可通过 TaskOption 指定具体的具体运行时刻：WithHour WithMinute WithSecond WithMillisecond
//   - 注意：任务添加后/执行前相同Key任务的添加操作会直接覆盖前一次添加的任务
func (ts *TaskManager) EveryMonthFirstDay(key string, handler TaskHandler, ops ...TaskOption) error {
	if !ts.isRuning.Load() {
		return ErrTaskClosed
	}
	if key == "" {
		return ErrTaskKeyEmpty
	}
	if handler == nil {
		return ErrTaskHandlerNil
	}
	op := parseTaskOption(ops...)
	y, m, _ := op.startAt.Date()
	err := ts.addTask(&Task{
		key:      key,
		interval: newTaskIntervalWithDate(taskIntervalMonthFirstDay, 0, y, int(m), 1, op.hour, op.minute, op.second, op.millisecond, 0),
		runMax:   int32(op.maxRuns),
		handler:  handler,
		timeout:  op.timeout,
		pc:       ts.getEntryPC(3, true),
	})
	releaseTaskOption(op)
	return err
}

// EveryMonthLastDay 创建周期性任务（每月最后一天运行一次）
//   - ops 可通过 TaskOption 指定具体的具体运行时刻：WithHour WithMinute WithSecond WithMillisecond
//   - 注意：任务添加后/执行前相同Key任务的添加操作会直接覆盖前一次添加的任务
func (ts *TaskManager) EveryMonthLastDay(key string, handler TaskHandler, ops ...TaskOption) error {
	if !ts.isRuning.Load() {
		return ErrTaskClosed
	}
	if key == "" {
		return ErrTaskKeyEmpty
	}
	if handler == nil {
		return ErrTaskHandlerNil
	}
	op := parseTaskOption(ops...)
	y, m, _ := op.startAt.Date()
	err := ts.addTask(&Task{
		key:      key,
		interval: newTaskIntervalWithDate(taskIntervalMonthLastDay, 0, y, int(m), 31, op.hour, op.minute, op.second, op.millisecond, 0),
		runMax:   int32(op.maxRuns),
		handler:  handler,
		timeout:  op.timeout,
		pc:       ts.getEntryPC(3, true),
	})
	releaseTaskOption(op)
	return err
}
